﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using JigLibX.Geometry;
using JigLibX.Physics;
using JigLibX.Collision;
using System.Xml;
using JigLibX.Math;
using PloobsEngine.Utils;
using PloobsEngine.Modelo;

namespace PloobsEngine.Physics.XJigLib
{
    public class TriangleMeshObject : JigLibXObject
    {
        TriangleMesh triangleMesh;        
        private bool isDeslocated;
        private string physicModelName = null;        

        public string PhysicModelName
        {
            get { return physicModelName; }
            set { physicModelName = value; }
        }
        

        /// <summary>
        /// Serialization
        /// </summary>
        public TriangleMeshObject()
        {
        }

        public TriangleMeshObject(IModelo m, Matrix orientation, Vector3 position, Vector3 scale)            
        {
            this.scale = scale;
            init(m, orientation, position, true);
        }

        public TriangleMeshObject(IModelo m, Matrix orientation, Vector3 position)
            :this(m,orientation,position,true)
        {
        }
        public TriangleMeshObject(IModelo m, Matrix orientation, Vector3 position,bool isDeslocated)
        {
            init(m, orientation, position, isDeslocated);
        }

        private void init(IModelo m, Matrix orientation, Vector3 position, bool isDeslocated)
        {
            this.isDeslocated = isDeslocated;                        
            Body = new Body();
            Body.Position = position;
            CollisionSkin = new CollisionSkin(null);
            triangleMesh = new TriangleMesh();
            List<Vector3> vertexList = new List<Vector3>();
            List<TriangleVertexIndices> indexList = new List<TriangleVertexIndices>();
            ExtractData(vertexList, indexList, m, isDeslocated);
            triangleMesh.CreateMesh(vertexList, indexList, 4, 1.0f);            
            CollisionSkin.AddPrimitive(triangleMesh, (int)MaterialTable.MaterialID.NormalRough);
            Body.MoveTo(position, orientation);
            CollisionSkin.ApplyLocalTransform(new Transform(position, orientation));            
            
            Body_.CollisionSkin = CollisionSkin;
        }


        /// <summary>
        /// Helper to get the vertex and index List from the model.
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="indices"></param>
        /// <param name="model"></param>
        private void ExtractData(List<Vector3> vertices, List<TriangleVertexIndices> indices, IModelo model,bool boneDeslocate)
        {

            Matrix[] bones_ = model.getBonesTransformation();            
            for (int i = 0; i < model.MeshNumber; i++)
            {                
                Matrix xform;
                if (boneDeslocate)
                {
                    xform = bones_[model.GetParentBoneIndex(i) - 1];
                }
                else
                {
                    xform = bones_[model.GetParentBoneIndex(i)];
                }
                BatchInformation[] bi = model.GetBatchInformation(i);
                for (int j = 0; j < bi.Length; j++)
                {
                    BatchInformation info = bi[j];                    

                    int offset = vertices.Count;
                    Vector3[] a = new Vector3[info.NumVertices];
                    model.GetVertexBuffer(i).GetData<Vector3>(info.StreamOffset + info.BaseVertex * info.VertexStride,a, 0, info.NumVertices, info.VertexStride);
                    xform = xform * Matrix.CreateScale(this.scale);
                    for (int k = 0; k != a.Length; ++k)
                    {                        
                        Vector3.Transform(ref a[k], ref xform, out a[k]);
                    }
                    vertices.AddRange(a);

                    if (model.GetIndexBuffer(i).IndexElementSize == IndexElementSize.SixteenBits)
                    {
                        short[] s = new short[info.PrimitiveCount * 3];
                        model.GetIndexBuffer(i).GetData<short>(info.StartIndex * 2, s, 0, info.PrimitiveCount * 3);
                        TriangleVertexIndices[] tvi = new TriangleVertexIndices[info.PrimitiveCount];
                        for (int k = 0; k != tvi.Length; ++k)
                        {
                            tvi[k].I0 = s[k * 3 + 2] + offset;
                            tvi[k].I1 = s[k * 3 + 1] + offset;
                            tvi[k].I2 = s[k * 3 + 0] + offset;
                        }
                        indices.AddRange(tvi);
                    }
                    else
                    {
                        int[] s = new int[info.PrimitiveCount * 3];
                        model.GetIndexBuffer(i).GetData<int>(info.StartIndex * 2, s, 0, info.PrimitiveCount * 3);
                        TriangleVertexIndices[] tvi = new TriangleVertexIndices[info.PrimitiveCount];
                        for (int k = 0; k != tvi.Length; ++k)
                        {
                            tvi[k].I0 = s[k * 3 + 2] + offset;
                            tvi[k].I1 = s[k * 3 + 1] + offset;
                            tvi[k].I2 = s[k * 3 + 0] + offset;
                        }
                        indices.AddRange(tvi);
                    }
                }
            }               
            
        }



        public override void DeSerialize(XmlNode node, EngineStuff engine, IModelo model, Vector3 position, Matrix rotation)
        {            
            bool desl = SerializerHelper.DeserializeBaseType<bool>("isDeslocated", node);
            physicModelName = SerializerHelper.DeserializeBaseType<string>("PhysicAuxiliarModelName", node);
            if (String.IsNullOrEmpty(physicModelName))
            {
                init(model, rotation, position, desl);                
            }
            else
            {
                IModelo md= new SimpleModel(physicModelName);
                md.LoadModelo();
                init(md, rotation, position, desl);                
            }
            this.isMotionLess = SerializerHelper.DeserializeBaseType<bool>("CanMove", node);
        }

        public override void Serialize(System.Xml.XmlTextWriter textWriter)
        {            
            SerializerHelper.SerializeBaseType<bool>(isDeslocated, "isDeslocated", textWriter);
            SerializerHelper.SerializeBaseType<bool>(isMotionLess, "CanMove", textWriter);
            SerializerHelper.SerializeBaseType<string>(physicModelName, "PhysicAuxiliarModelName", textWriter);
        }
        public override PhysicObjectTypes PhysicObjectTypes
        {
            get { return PhysicObjectTypes.TRIANGLEMESHOBJECT; }
        }
     
    }
}
